package com.zxy.supplier_system.server.service

import com.zxy.supplier_system.server.controller.MarketCheckInventoryRecordController
import com.zxy.supplier_system.server.controller.MarketCheckInventoryRecordsItemController
import com.zxy.supplier_system.server.entity.*
import com.zxy.supplier_system.server.repository.*
import com.zxy.supplier_system.server.utils.*
import org.springframework.core.io.InputStreamResource
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.domain.Specification
import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.server.ResponseStatusException
import java.time.LocalDate
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter

@Service
class MarketCheckInventoryRecordService(
    private val marketCheckInventoryRecordRepository: MarketCheckInventoryRecordRepository,
    private val marketCheckInventoryRecordItemRepository: MarketCheckInventoryRecordItemRepository,
    private val employeeRepository: EmployeeRepository,
    private val systemServiceService: SystemServiceService,
    private val goodsRepository: GoodsRepository,
    private val employeeService: EmployeeService,
    private val employeeOperateRecordService: EmployeeOperateRecordService,
    private val marketRepository: MarketRepository,
) {

    @Transactional(readOnly = true)
    fun queryMarketCheckInventoryRecord(
        queryMarketCheckInventoryRecordRequest: MarketCheckInventoryRecordController.QueryMarketCheckInventoryRecordRequest,
        pageable: Pageable
    ): Page<MarketCheckInventoryRecordController.QueryMarketCheckInventoryRecordResponse> {

        val employee = employeeService.getRequiredCurrentEmployee()
        return this.marketCheckInventoryRecordRepository.findAll(
            Specification.allOf(
                MarketCheckInventoryRecordRepository.Specifications.endDateLessThanOrEqualTo(
                    queryMarketCheckInventoryRecordRequest.endDate
                ),
                MarketCheckInventoryRecordRepository.Specifications.startDateGreaterThanOrEqualTo(
                    queryMarketCheckInventoryRecordRequest.startDate
                ),
                MarketCheckInventoryRecordRepository.Specifications.employeeIn(queryMarketCheckInventoryRecordRequest.employeeIds),
                MarketCheckInventoryRecordRepository.Specifications.submittedEquals(
                    queryMarketCheckInventoryRecordRequest.submitted
                ),
                MarketCheckInventoryRecordRepository.Specifications.inMarketIds(queryMarketCheckInventoryRecordRequest.marketIds),
            ).and(
                if (employee.authorities.contains(Authority.卖场_订单_查询所有)) {
                    Specification.where(null)
                } else {
                    MarketCheckInventoryRecordRepository.Specifications.marketSaleManagerEqual(employee.id!!)
                        .or(MarketCheckInventoryRecordRepository.Specifications.marketShoppingGuidesContains(employee.id!!))
                }
            ), pageable
        ).map { marketCheckInventoryRecord ->
            MarketCheckInventoryRecordController.QueryMarketCheckInventoryRecordResponse(
                marketCheckInventoryRecord.id,
                marketCheckInventoryRecord.createdBy.let {
                    IntIdAndName(it.id!!, it.name)
                },
                marketCheckInventoryRecord.createdDateTime,
                marketCheckInventoryRecord.items.size,
                marketCheckInventoryRecord.name,
                marketCheckInventoryRecord.submittedDateTime,
                marketCheckInventoryRecord.market.toIntIdAndName(),
            )
        }
    }

    @Transactional
    fun createMarketCheckInventoryRecord(
        request: MarketCheckInventoryRecordController.CreateMarketCheckInventoryRecordRequest
    ): MarketCheckInventoryRecordController.QueryMarketCheckInventoryRecordResponse {
        val employee = employeeService.getRequiredCurrentEmployee()
        val marketCheckInventoryRecord =
            this.marketCheckInventoryRecordRepository.save(MarketCheckInventoryRecord().apply {
                this.name = request.name ?: (LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "盘点")
                this.market =
                    marketRepository.findByShoppingGuidesContainsOrSaleManagerEqualAndId(employee, request.marketId)
                        .orElse404()
            })
        this.employeeOperateRecordService.createMarketCheckInventoryRecord(marketCheckInventoryRecord)
        return MarketCheckInventoryRecordController.QueryMarketCheckInventoryRecordResponse(
            marketCheckInventoryRecord.id,
            marketCheckInventoryRecord.createdBy.let {
                IntIdAndName(it.id!!, it.name)
            },
            marketCheckInventoryRecord.createdDateTime,
            0,
            marketCheckInventoryRecord.name,
            null,
            marketCheckInventoryRecord.market.toIntIdAndName(),
        )
    }

    @Transactional
    fun updateOrderGoods(
        id: Int,
        updateOrderGoodsRequest: MarketCheckInventoryRecordController.UpdateOrderGoodsRequest
    ) {
        val marketCheckInventoryRecord = this.marketCheckInventoryRecordRepository.findByIdOrNull(id).orElse404()
        marketCheckInventoryRecord.items.clear()
        marketCheckInventoryRecord.items.addAll(
            updateOrderGoodsRequest.items.mapIndexed { index, it ->
                MarketCheckInventoryRecordItem().apply {
                    this.record = marketCheckInventoryRecord
                    this.amount = it.amount
                    this.goods =
                        goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(it.id, systemServiceService.getCurrentSystemService())
                            .orElse404()
                    this.sequence = index
                }
            }
        )
        this.marketCheckInventoryRecordRepository.save(marketCheckInventoryRecord)
    }

    @Transactional(readOnly = true)
    fun getMarketCheckInventoryRecordDetail(
        id: Int,
        includeItems: Boolean
    ): MarketCheckInventoryRecordController.MarketCheckInventoryRecordDetail {
        val marketCheckInventoryRecord = this.marketCheckInventoryRecordRepository.findByIdOrNull(id).orElse404()
        return MarketCheckInventoryRecordController.MarketCheckInventoryRecordDetail(
            marketCheckInventoryRecord.id,
            marketCheckInventoryRecord.createdDateTime,
            marketCheckInventoryRecord.name,
            IntIdAndName(marketCheckInventoryRecord.createdBy.id!!, marketCheckInventoryRecord.createdBy.name),
            if (!includeItems) emptyList() else marketCheckInventoryRecord.items.map { item ->
                val goods = item.goods
                goods.let { it ->
                    MarketCheckInventoryRecordController.MarketCheckInventoryRecordItemResponse(
                        it.id,
                        it.name, it.barcode,
                        it.price,
                        it.amount,
                        it.createdDateTime,
                        it.lastModifiedDateTime,
                        it.brand?.toIntIdAndName(),
                        it.category?.toIntIdAndName(),
                        it.factory?.toIntIdAndName(),
                        item.amount,
                        it.boxSpecification,
                        it.registrationCertificateNumber,
                        it.executiveStandard,
                        it.recordFilingNumber,
                    )
                }
            },
            marketCheckInventoryRecord.submittedDateTime,
            marketCheckInventoryRecord.market.toIntIdAndName(),
        )
    }

    @Transactional
    fun addItem(
        orderId: Int,
        addMarketCheckInventoryRecordsItemRequest: MarketCheckInventoryRecordsItemController.AddMarketCheckInventoryRecordsItemRequest
    ) {
        val marketCheckInventoryRecord = this.marketCheckInventoryRecordRepository.findByIdOrNull(orderId).orElse404()
        marketCheckInventoryRecord.items.add(MarketCheckInventoryRecordItem().apply {
            this.record = marketCheckInventoryRecord
            this.goods = goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(
                addMarketCheckInventoryRecordsItemRequest.goodsId,
                systemServiceService.getCurrentSystemService()
            ).orElse404()
            this.amount = addMarketCheckInventoryRecordsItemRequest.amount
            this.sequence = marketCheckInventoryRecord.items.nextSequence
        })
        this.marketCheckInventoryRecordRepository.save(marketCheckInventoryRecord)
        this.employeeOperateRecordService.addMarketCheckInventoryRecordItem(marketCheckInventoryRecord)
    }

    @Transactional
    fun deleteItem(orderId: Int, goodsId: Int) {
        this.marketCheckInventoryRecordItemRepository.deleteByRecord_IdAndGoods_IdAndGoods_SystemService(
            orderId,
            goodsId,
            systemServiceService.getCurrentSystemService()
        )
    }

    @Transactional
    fun updateItemAmount(
        goodsId: Int,
        orderId: Int,
        updateItemAmountRequest: MarketCheckInventoryRecordsItemController.UpdateItemAmountRequest
    ) {
        val marketCheckInventoryRecordItem =
            this.marketCheckInventoryRecordItemRepository.findByIdOrNull(
                MarketCheckInventoryRecordItem.UnionId().apply {
                    this.record = orderId
                    this.goods = goodsId
                }).orElse404()
        marketCheckInventoryRecordItem.amount = updateItemAmountRequest.amount
        this.marketCheckInventoryRecordItemRepository.save(marketCheckInventoryRecordItem)
        this.employeeOperateRecordService.updateMarketCheckInventoryRecordItemAmount(marketCheckInventoryRecordItem)
    }

    fun getAllEmployees(): List<IntIdAndName> {
        return this.employeeRepository.findAll(
            EmployeeRepository.Specifications.haveCreatedMarketCheckInventoryRecord().and(
                SystemServiceRepository.Specifications.systemServiceEquals(
                    { root -> root },
                    systemServiceService.getCurrentSystemService()
                )
            )
        ).map {
            IntIdAndName(it.id!!, it.name)
        }
    }


    fun queryOrderItemsInOrder(
        orderId: Int,
        pageable: Pageable
    ): Page<MarketCheckInventoryRecordsItemController.MarketCheckInventoryRecordItemSummary> {
        return this.marketCheckInventoryRecordItemRepository.findAllByRecord_IdOrderBySequenceAsc(orderId, pageable)
            .map {
                val goods = it.goods
                MarketCheckInventoryRecordsItemController.MarketCheckInventoryRecordItemSummary.fromGoodsAndAmount(
                    goods,
                    it.amount
                )
            }
    }

    fun exists(orderId: Int, goodsId: Int): Boolean {
        return this.marketCheckInventoryRecordItemRepository.existsByRecord_IdAndGoods_Id(orderId, goodsId)
    }

    fun exists(orderId: Int, barcode: String): Boolean {
        return this.marketCheckInventoryRecordItemRepository.existsByRecord_IdAndGoods_Barcode(orderId, barcode)
    }


    @Transactional
    fun addItem(
        orderId: Int,
        barcode: String
    ): MarketCheckInventoryRecordsItemController.MarketCheckInventoryRecordItemSummary {
        val currentSystemService = systemServiceService.getCurrentSystemService()
        val marketCheckInventoryRecord = this.marketCheckInventoryRecordRepository.findByIdAndCreatedBy_SystemService(
            orderId,
            currentSystemService
        ).orElse404()
        val goods =
            goodsRepository.findByBarcodeAndSystemServiceAndDisableIsFalse(barcode, currentSystemService)
                .orElse404()
        return orderItemInOrderSummary(marketCheckInventoryRecord, goods)
    }

    @Transactional
    fun submitMarketCheckInventoryRecord(id: Int) {
        val marketCheckInventoryRecord = this.marketCheckInventoryRecordRepository.findByIdAndCreatedBy_SystemService(
            id,
            systemServiceService.getCurrentSystemService()
        ).orElse404()
        if (marketCheckInventoryRecord.submittedDateTime == null) {
            marketCheckInventoryRecord.submittedDateTime = OffsetDateTime.now()
            marketCheckInventoryRecord.items.forEach {
                it.goods.amount += it.amount
            }
            this.goodsRepository.saveAll(marketCheckInventoryRecord.items.map { it.goods })
            this.marketCheckInventoryRecordRepository.save(marketCheckInventoryRecord)
            this.employeeOperateRecordService.submitMarketCheckInventoryRecord(marketCheckInventoryRecord)
        } else {
            throw ResponseStatusException(HttpStatus.BAD_REQUEST)
        }
    }

    @Transactional
    fun addItem(
        orderId: Int,
        goodsId: Int
    ): MarketCheckInventoryRecordsItemController.MarketCheckInventoryRecordItemSummary {
        val currentSystemService = systemServiceService.getCurrentSystemService()
        val marketCheckInventoryRecord = this.marketCheckInventoryRecordRepository.findByIdAndCreatedBy_SystemService(
            orderId,
            currentSystemService
        ).orElse404()
        val goods =
            goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(goodsId, currentSystemService)
                .orElse404()
        return orderItemInOrderSummary(marketCheckInventoryRecord, goods)
    }

    private fun orderItemInOrderSummary(
        marketCheckInventoryRecord: MarketCheckInventoryRecord,
        goods: Goods
    ): MarketCheckInventoryRecordsItemController.MarketCheckInventoryRecordItemSummary {
        val amount = 1
        marketCheckInventoryRecord.items.add(MarketCheckInventoryRecordItem().apply {
            this.record = marketCheckInventoryRecord
            this.goods =
                goods
            this.amount = amount
            this.sequence = marketCheckInventoryRecord.items.nextSequence
        })
        this.marketCheckInventoryRecordRepository.save(marketCheckInventoryRecord)
        this.employeeOperateRecordService.addMarketCheckInventoryRecordItem(marketCheckInventoryRecord)
        return MarketCheckInventoryRecordsItemController.MarketCheckInventoryRecordItemSummary.fromGoodsAndAmount(
            goods,
            amount
        )
    }

    fun getAllMarketCheckInventoryRecordCreatedDateRange(): NullableLocalDateRange {
        return NullableLocalDateRange(
            this.marketCheckInventoryRecordRepository.findMaxCreatedDateTime()?.toLocalDate(),
            this.marketCheckInventoryRecordRepository.findMaxCreatedDateTime()?.toLocalDate()
        )
    }

    fun getAllMarkets(): List<IntIdAndName> {
        return this.marketRepository.findAll(
            MarketRepository.Specifications.haveCheckInventoryRecord().and(
                SystemServiceRepository.Specifications.systemServiceEquals(
                    { root -> root },
                    systemServiceService.getCurrentSystemService()
                )
            )
        ).map {
            IntIdAndName(it.id, it.name)
        }
    }

    fun getMarketCheckInventoryRecordCreatedEmployeesByMarketId(id: Int): List<IntIdAndName> {
        return this.employeeRepository.findAll(
            EmployeeRepository.Specifications.haveCreatedMarketCheckInventoryRecord().and(
                SystemServiceRepository.Specifications.systemServiceEquals(
                    { root -> root },
                    systemServiceService.getCurrentSystemService()
                )
            ).and(EmployeeRepository.Specifications.createdMarketCheckInventoryRecord_MarketIdEqual(id))
        ).map {
            IntIdAndName(it.id!!, it.name)
        }
    }

    @Transactional(readOnly = true)
    fun exportToExcel(id: Int, fields: Array<GoodsExcelField>): InputStreamResource {
        val marketCheckInventoryRecord = this.marketCheckInventoryRecordRepository.findByIdOrNull(id).orElse404()
        val items = marketCheckInventoryRecord.items
        val workbook = exportGoodsDataToWorkbook(fields, items.map { item ->
            item.goods.excelRow
        })
        return workbookToInputStreamResource(workbook)
    }

    @Transactional
    fun delete(id: Int) {
        this.marketCheckInventoryRecordRepository.deleteBySubmittedDateTimeIsNullAndIdAndMarket_SystemService(
            id,
            systemServiceService.getCurrentSystemService()
        )
    }

}